package com;

import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Random;
import javax.swing.JPanel;

/**
 * Diseño del area donde se realizará el juego. 
 * 
 * @author Alex Munoz Dominguez and Mauricio Orellana Galaz
 * @version 13-07-2012
 */
public class ArenaDeJuego extends JPanel implements MouseListener, MouseMotionListener
{
    private Figure[][] matrizSolu;
    private Figure[][] matrizAzar;
    private Grid zonaResuelta;
    private Grid zonaAzar;
    private Point point, lugarM;
    private Calendar calendario;
    private int size;
    private boolean inicioJuego;
    
    /**
     * Constructor de objetos de la clase ArenaDeJuego.
     * 
     * @param size Tamaño del tablero de juego.
     */
    public ArenaDeJuego(int size)
    {
        this.size = size;
        this.matrizAzar = new Figure[size][size];
        this.matrizSolu = new Figure[size][size];
        this.zonaResuelta = new Grid(size);
        this.zonaAzar = new Grid(size);
        this.setLayout(null);
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        this.calendario = new GregorianCalendar();
        this.createFichas();
    }

    @Override
    public void mouseClicked(MouseEvent me) {
        
    }

    @Override
    public void mousePressed(MouseEvent me)
    {
        Figure f;
        if((f = this.getPresed(me.getPoint())) != null){
            f.setPressed(true);
            this.point = new Point(f.getX(), f.getY());
            if(this.zonaResuelta.verZona(me.getPoint())){ // si se presiona una figura del panel zona resulta, esta se elimina
                this.zonaResuelta.setFiguras(this.zonaResuelta.getPuntoGuardar().x, this.zonaResuelta.getPuntoGuardar().y, null);
                this.lugarM = new Point(this.zonaResuelta.getPuntoGuardar().x,this.zonaResuelta.getPuntoGuardar().y);
            }
        }
    }

    @Override
    public void mouseReleased(MouseEvent me)
    {
        Figure fig = null;
        if((fig = getPresed())!= null) // se obtiene la figura presionada
        {
            boolean dentroDelPanel = this.zonaResuelta.verZona(me.getPoint());//aqui cambiamos el punto de la matriz
            if(dentroDelPanel) 
            {
                boolean movimientoValido = this.zonaResuelta.setFigure(fig, true);
                if(!movimientoValido)
                {
                    fig.setBounds(this.point.x, this.point.y, 100, 100);
                    if(this.zonaResuelta.verZona(point))
                    {
                    this.zonaResuelta.setFiguras(this.lugarM.x, this.lugarM.y, fig);
                    } 
                }
            }
            else{  // cuando no está en el grid Izquierdo
               fig.setBounds(this.point.x, this.point.y, 100, 100);
            }
            repaint();
            fig.setPressed(false);
            
            if(isJuegoTerminado()){
                Calendar aux = new GregorianCalendar();  
                int hr = aux.get(Calendar.HOUR) - this.calendario.get(Calendar.HOUR);
                int min = aux.get(Calendar.MINUTE) - this.calendario.get(Calendar.MINUTE);
                int seg = aux.get(Calendar.SECOND) - this.calendario.get(Calendar.SECOND);

                Score s = new Score();
                s.registrarPuntaje(hr, min, seg);
                Dialog d = new Dialog(CommandNames.SCORE_TITLE);
                d.setcontenido(new Score());
                d.setLocationRelativeTo(this);
                d.setVisible(true); 
            }
        }
    }

    @Override
    public void mouseEntered(MouseEvent me) {
        
    }

    @Override
    public void mouseExited(MouseEvent me) {
        
    }

    @Override
    public void mouseDragged(MouseEvent me) {
        Figure fig = null;
        if((fig = getPresed() )!= null)
        {
            fig.setBounds(me.getX()-50, me.getY()-50, 100, 100);
            this.repaint();
        }
    }

    @Override
    public void mouseMoved(MouseEvent me) {
        
    }

    /**
     * Genera las fichas aleatorias que serán usadas en la partida y las guarda en dos matrices, en una ordenadas y
     * en la otra desordenadas.
     */
    private void createFichas()
    {
        this.inicioJuego = false;
        Figure matriz[][] = new Figure[this.size][this.size];
        Random s = new Random();
        
        for(int i = 0 ; i< this.size; i++)
        {
            for(int j = 0 ; j < this.size; j++)
            {
                Figure figuraTemp = new Figure(s.nextInt(10),s.nextInt(10),s.nextInt(10),s.nextInt(10));
                matriz[i][j] = figuraTemp;
                if(j>0)
                {
                    matriz[i][j].setLeft(matriz[i][j-1].getRight());
                }
                if(i>0)
                {
                    matriz[i][j].setUp(matriz[i-1][j].getDown());
                }
            }
        }
        this.matrizSolu = matriz.clone();
        int nAzar = s.nextInt(size*size)+1;
        int i = 0;
        int x1,y1,x2,y2;
        
        while(i<nAzar)
        {
            x1 = s.nextInt(size);
            y1 = s.nextInt(size);
            x2 = s.nextInt(size);
            y2 = s.nextInt(size);
            Figure temp = matriz[x1][y1];
            matriz[x1][y1] = matriz[x2][y2];
            matriz[x2][y2] = temp;  
            i++;
        }
        this.matrizAzar = matriz.clone();
        
        this.dibujar();
    }

    /**
     * Dibuja las fichas del juego y los dos tableros que se usan en el juego.
     */
    private void dibujar()
    {        
        
        for( int i = 0; i < this.size ; i++ )
        {
            for( int j = 0 ; j < this.size ; j++ )
            {
                this.add(this.matrizAzar[i][j]);
                this.matrizAzar[i][j].setBounds((size)*100+50+(j+1)*100,(i)*100 +50, 100,100);
            }
        }
        this.add(this.zonaAzar);
        this.zonaAzar.setBounds((size+1)*100 + 50, 50, 101*size, 101*size);
        this.add(this.zonaResuelta);
        this.zonaResuelta.setBounds(50, 50, 101*size, 101*size);        
    }

    /**
     * Retorna la dimension del tablero de juego actual.
     * 
     * @return la dimension del tablero. 
     */
    public int getSizeM() {
        return size;
    }

    /**
     * Modifica la dimension del tablero de juego.
     * 
     * @param size El nuevo tamaño del tablero. 
     */
    public void setSizeM(int size) {
        this.size = size;
    }

    /**
     * Retorna la figura que se encuentra presionada.
     * 
     * @param point Un punto de la ventana.
     * @return Retorna la figura que se encuentra presionada, null si ninguna figura se encuentra presionada.
     */
    private Figure getPresed(Point point) {
        for(int i=0; i<size; i++){
            for(int j=0; j<size; j++){
                if(matrizAzar[i][j].isPresed(point)){
                    return matrizAzar[i][j];
                }
            }
        }
        return null;
    }
    
    /**
     * Retorna la figura que se encuentra presiona.
     * 
     * @return Retorna la figura que se encuentra presionda, null si ninguna figura se encuentra presionda.
     */
    private Figure getPresed(){
        for(int i=0; i<size; i++){
            for(int j=0; j<size; j++){
                if(matrizAzar[i][j].isPressed()){
                    return matrizAzar[i][j];
                }
            }
        }
        return null;
    }
    

    /**
     * Retorna verdadero si el juego ya está resuelto.
     * 
     * @return Verdadero si el juego está resuelto, falso si aun hay fichas que no han sido ubicadas.
     */
    private boolean isJuegoTerminado()
    {
        int fichasCorrectas = 0;
        for(int i=0; i<this.size; i++){
            for(int j=0; j<this.size; j++){
                if(zonaResuelta.getFiguras(i, j) != null){
                    fichasCorrectas++;
                }
            }
        }
        if(fichasCorrectas == (this.size * this.size)){  // si la matriz estA completa
            return true;
        }
        return false;
    }
}
